import { spawn, ChildProcess } from 'child_process';
import { EventEmitter } from 'events';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const ffmpegPath = (require('@ffmpeg-installer/ffmpeg') as any).path.replace('app.asar', 'app.asar.unpacked');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ffprobePath = (require('@ffprobe-installer/ffprobe') as any).path.replace('app.asar', 'app.asar.unpacked');

console.log('ffmpegPath:', ffmpegPath);
console.log('ffprobePath:', ffprobePath);

/**
 * 将时间字符串 HH:MM:SS.xx 转秒
 */
function parseTimeToSeconds(timeStr: string): number {
  const parts = timeStr.split(':').map(Number);
  return parts[0] * 3600 + parts[1] * 60 + parts[2];
}

/**
 * 解析帧率字符串 (例如 "30/1" 或 "24000/1001")
 */
function parseFrameRate(frameRate: string): number {
  if (!frameRate) return 0;
  const parts = frameRate.split('/');
  if (parts.length === 2) {
    const numerator = parseFloat(parts[0]);
    const denominator = parseFloat(parts[1]);
    return denominator !== 0 ? numerator / denominator : 0;
  }
  return parseFloat(frameRate) || 0;
}

/**
 * 获取视频/音频总时长
 */
export function getDuration(file: string): Promise<number> {
  return new Promise((resolve, reject) => {
    const ffprobe = spawn(ffprobePath, [
      '-v',
      'error',
      '-show_entries',
      'format=duration',
      '-of',
      'default=noprint_wrappers=1:nokey=1',
      file,
    ]);

    let output = '';
    ffprobe.stdout.on('data', (data: Buffer) => (output += data.toString()));
    ffprobe.on('error', (err: Error) => reject(err));
    ffprobe.on('close', () => resolve(parseFloat(output) || 0));
  });
}

/**
 * 获取完整视频信息
 */
export function getVideoInfo(file: string): Promise<any> {
  return new Promise((resolve, reject) => {
    const ffprobe = spawn(ffprobePath, ['-v', 'error', '-show_format', '-show_streams', '-of', 'json', file]);

    let output = '';
    ffprobe.stdout.on('data', (data: Buffer) => (output += data.toString()));
    ffprobe.stderr.on('data', (data: Buffer) => console.error('ffprobe error:', data.toString()));
    ffprobe.on('error', (err: Error) => reject(err));
    ffprobe.on('close', (code: number | null) => {
      if (code !== 0) {
        reject(new Error(`ffprobe exited with code ${code}`));
      } else {
        try {
          const info = JSON.parse(output);
          const videoStream = info.streams.find((s: any) => s.codec_type === 'video');
          const audioStream = info.streams.find((s: any) => s.codec_type === 'audio');

          resolve({
            duration: parseFloat(info.format.duration) || 0,
            size: parseInt(info.format.size) || 0,
            bitRate: parseInt(info.format.bit_rate) || 0,
            formatName: info.format.format_name || '',
            video: videoStream
              ? {
                  codec: videoStream.codec_name,
                  width: videoStream.width,
                  height: videoStream.height,
                  fps: parseFrameRate(videoStream.r_frame_rate),
                  bitRate: parseInt(videoStream.bit_rate) || 0,
                }
              : null,
            audio: audioStream
              ? {
                  codec: audioStream.codec_name,
                  sampleRate: parseInt(audioStream.sample_rate) || 0,
                  channels: audioStream.channels,
                  bitRate: parseInt(audioStream.bit_rate) || 0,
                }
              : null,
          });
        } catch (err) {
          reject(err);
        }
      }
    });
  });
}

/**
 * 进度回调接口
 */
export interface ProgressCallback {
  start?: (data: { cmd: string }) => void;
  progress?: (data: {
    percent: number | null;
    time: number | null;
    frame: number | null;
    fps: number | null;
    bitrate: string | null;
    speed: string | null;
  }) => void;
  error?: (data: { msg: string }) => void;
  end?: () => void;
}

/**
 * FFmpeg 任务类
 */
export class FFmpegTask extends EventEmitter {
  private process: ChildProcess | null = null;
  private totalDuration = 0;

  constructor(private args: string[], private inputFile?: string) {
    super();
  }

  /**
   * 开始执行任务
   */
  async start(): Promise<void> {
    // 获取输入文件时长
    if (this.inputFile) {
      try {
        this.totalDuration = await getDuration(this.inputFile);
      } catch (e) {
        this.totalDuration = 0;
      }
    }

    console.log(`ffmpeg ${this.args.join(' ')}`);

    this.process = spawn(ffmpegPath, this.args, {
      stdio: ['ignore', 'pipe', 'pipe'],
    });

    this.emit('start', { cmd: `ffmpeg ${this.args.join(' ')}` });

    this.process.stderr?.setEncoding('utf8');
    this.process.stderr?.on('data', (chunk: string) => {
      this.parseProgress(chunk);
    });

    this.process.on('error', (err: Error) => {
      console.log('ffmpeg process error:', err.message);
      this.emit('error', { msg: err.message });
    });

    this.process.on('close', (code: number | null) => {
      if (code !== 0) {
        console.log('ffmpeg process close with error:', code);
        this.emit('error', { msg: `ffmpeg exited with code ${code}` });
      } else {
        this.emit('progress', { percent: 100 });
        this.emit('end');
      }
      this.process = null;
    });

    return new Promise<void>((resolve, reject) => {
      this.once('end', resolve);
      this.once('error', (data: { msg: string }) => reject(new Error(data.msg)));
    });
  }

  /**
   * 解析进度信息
   */
  private parseProgress(chunk: string) {
    const lines = chunk.split(/\r?\n/);
    lines.forEach((line) => {
      const timeMatch = line.match(/time=(\d{2}:\d{2}:\d{2}(?:\.\d+)?)/);
      const frameMatch = line.match(/frame=\s*(\d+)/);
      const fpsMatch = line.match(/fps=\s*([\d.]+)/);
      const bitrateMatch = line.match(/bitrate=\s*([\d.]+\w*bits\/s)/);
      const speedMatch = line.match(/speed=\s*([\d.]+x)/);

      if (timeMatch || frameMatch) {
        const currentTime = timeMatch ? parseTimeToSeconds(timeMatch[1]) : null;
        const percent =
          currentTime && this.totalDuration ? Math.min((currentTime / this.totalDuration) * 100, 99) : null;

        this.emit('progress', {
          percent,
          time: currentTime,
          frame: frameMatch ? parseInt(frameMatch[1], 10) : null,
          fps: fpsMatch ? parseFloat(fpsMatch[1]) : null,
          bitrate: bitrateMatch ? bitrateMatch[1] : null,
          speed: speedMatch ? speedMatch[1] : null,
        });
      }
    });
  }

  /**
   * 取消任务
   */
  cancel() {
    if (this.process) {
      this.process.kill('SIGKILL');
      this.process = null;
      this.emit('error', { msg: 'Task cancelled by user' });
    }
  }
}

export { ffmpegPath, ffprobePath };
